/*-
 * Copyright (c) 2009-2010 David Forsythe.
 * See LICENSE file for license details.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <err.h>

#include "internal.h"
#include "pkg.h"

#define MAGIC                           0x09999999

/* Create and initialize a pkg for use with libpkg. */
struct pkg *
pkg_create(void)
{
        struct pkg *p;

        p = calloc(1, sizeof(*p));
        if (p == NULL) {
                return (NULL);
        }
        
        /* Set magic. */
        p->magic = MAGIC;
        /* No property list. */
        p->plist = NULL;
        p->vt = NULL;
        p->mt = NULL;
        TAILQ_INIT(&p->scripts);
        strcpy(p->key, "");
        
        return (p);
}

/*
 * Check the magic number in a package.  Frontends should use this to check for
 * package validity before modifying properties.  The package database functions
 * will always use this to check the validity of a package before adding it to
 * disk or copying information into it.
 */
void
_pkg_check_magic(struct pkg *p, const char *func)
{
        if (p->magic != MAGIC) {
                PKG_CLIENT_CRASH(func, "magic number mismatch.");
        }
}

/* Clear all fields in a package except for its vtables. */
void
pkg_clear(struct pkg *p)
{
        struct _script *s;

        if (p->plist != NULL) {
                pkg_property_release(p->plist);
                p->plist = NULL;
        }
        while (!TAILQ_EMPTY(&p->scripts)) {
                s = TAILQ_FIRST(&p->scripts);
                TAILQ_REMOVE(&p->scripts, s, next);
                free(s->pathname);
                free(s);
        }
        TAILQ_INIT(&p->scripts);
}

void
pkg_release(struct pkg *p)
{
        pkg_clear(p);
        free(p);
}

/* Retrieve the Plist from a package.  If there is no Plist, it is created. */
struct pkg_property *
pkg_get_property_list(struct pkg *p)
{
        /* "plist" could just be "" */
        if (p->plist == NULL) {
                p->plist = pkg_property_plist_create("plist");
        }
        return (p->plist);
}

/* 
 * Get and set the name of the key that this package uses when registering in a
 * package database.  The key must be in the root dictionary of the package
 * property list.  Making it something other than a PLSTRING is probably pretty
 * stupid, because most databases probably won't have logic to interpret other
 * types of property nodes.
 */
const char *
pkg_get_key(struct pkg *p)
{
        return (p->key);
}

void
pkg_set_key(struct pkg *p, const char *key)
{
        if (key == NULL) {
                PKG_CLIENT_CRASH(__func__, "Invalid input.  key is NULL");
        }
        strcpy(p->key, key);
}

void
pkg_set_parser_writer_vtable(struct pkg *p, struct parser_writer_vtable *vt) 
{
        p->vt = vt;
}

void
pkg_set_meta_entry_table(struct pkg *p, struct meta_entry *mt)
{
        p->mt = mt;
}

struct meta_entry *
pkg_get_meta_entry_table(struct pkg *p)
{
        return (p->mt);
}

int
pkg_parse_FILE(struct pkg *p, FILE *fp, const char *metaname)
{
        if (p->vt == NULL) {
                PKG_CLIENT_CRASH(__func__, "Callback unsupported.");
        }
        return (p->vt->parse_FILE(p, fp, metaname));
}

int
pkg_parse_fd(struct pkg *p, int fd, size_t size, const char *metaname)
{
        if (p->vt == NULL) {
                PKG_CLIENT_CRASH(__func__, "Callback unsupported.");
        }
        return (p->vt->parse_fd(p, fd, size, metaname));
}

int
pkg_parse(struct pkg *p, char *data, const char *metaname)
{
        if (p->vt == NULL) {
                PKG_CLIENT_CRASH(__func__, "Callback unsupported.");
        }
        return (p->vt->parse(p, data, metaname));
}

int
pkg_write_FILE(struct pkg *p, FILE *fp, const char *metaname)
{
        return (p->vt->write_FILE(p, fp, metaname));
}

int
pkg_register_script(struct pkg *p, const char *name, const char *pathname)
{
        struct _script *script;
        

        TAILQ_FOREACH(script, &p->scripts, next) {
                if (STRMATCH(script->name, name)) {
                        TAILQ_REMOVE(&p->scripts, script, next);
                        free(script->pathname);
                        free(script);
                        break;
                }
        }

        script = calloc(1, sizeof(*script));
        if (script == NULL) {
                return (PKG_NOT_OK);
        }
        script->name = name;
        script->pathname = strdup(pathname);
        TAILQ_INSERT_TAIL(&p->scripts, script, next);
        return (PKG_OK);
}

const char *
pkg_get_script_pathname(struct pkg *p, const char *name)
{
        struct _script *script;
        TAILQ_FOREACH(script, &p->scripts, next) {
                if (strcmp(script->name, name) == 0) {
                        return (script->pathname);
                }
        }
        return (NULL);
}
